Add a utility function to translate coordinates relative to one widget's
authorOwen Taylor <otaylor@redhat.com>
Tue, 16 Oct 2001 21:02:24 +0000 (21:02 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Tue, 16 Oct 2001 21:02:24 +0000 (21:02 +0000)
Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>

* gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
a utility function to translate coordinates relative to
one widget's allocation to coordinates relative to another
widget's allocation.

* gtk/gtkradiobutton.c: Add a special ->focus() implementation
that:
 - only accepts external focus if there is no active
       member of the group or the button is active.
 - makes arrow keys move the active button as well
   as the focus
 - make tab tab out directly.
This makes a radio button group act as a single focus location.
(#53577).

* gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
it was only a small optimization that didn't matter and made
things more complicated.

* gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
sorter for FOCUS_TAB as we did for the other focus directions,
sort by center of widgets, not upper-left corner. (Shouldn't
matter in general.)

* gtk/gtkcontainer.c: Restructure code to remove duplicate code
from the different types of focusing: encapsulate sorting the
widgets for the focus direction into one routine
(gtk_container_focus_sort()) and then share the work of moving the
focus between the different focus directions.

* gtk/gtkcontainer.c: Fix bug where arrow navigation might not
work correctly with focus chains containing non-immediate
children. Sorting was being done using allocation coordinates for
each widget in the focus chain, and if there were intermediate
window-widgets, these allocations would not be in the same
coordinate system.

ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/gtkcontainer.c
gtk/gtkradiobutton.c
gtk/gtkwidget.c
gtk/gtkwidget.h

index 3b48967b838e282f8383176166ddf708f41f57fa..072e49aa662b465f262d711150da8aa9d3fe0eba 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,48 @@
+Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
+       a utility function to translate coordinates relative to
+       one widget's allocation to coordinates relative to another
+       widget's allocation.
+
+       * gtk/gtkradiobutton.c: Add a special ->focus() implementation
+       that:
+        - only accepts external focus if there is no active
+          member of the group or the button is active.
+        - makes arrow keys move the active button as well
+          as the focus
+        - make tab tab out directly.
+       This makes a radio button group act as a single focus location.
+       (#53577).
+       
+       * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
+       it was only a small optimization that didn't matter and made
+       things more complicated.
+
+       * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
+       sorter for FOCUS_TAB as we did for the other focus directions,
+       sort by center of widgets, not upper-left corner. (Shouldn't
+       matter in general.)
+
+       * gtk/gtkcontainer.c: Restructure code to remove duplicate code
+       from the different types of focusing: encapsulate sorting the
+       widgets for the focus direction into one routine
+       (gtk_container_focus_sort()) and then share the work of moving the
+       focus between the different focus directions.
+
+       * gtk/gtkcontainer.c: Fix bug where arrow navigation might not
+       work correctly with focus chains containing non-immediate
+       children. Sorting was being done using allocation coordinates for
+       each widget in the focus chain, and if there were intermediate
+       window-widgets, these allocations would not be in the same
+       coordinate system.
+
 Tue Oct 16 15:12:26 2001  Jonathan Blandford  <jrb@redhat.com>
 
        * gtk/gtkoptionmenu.c (gtk_option_menu_mnemonic_activate): add a
        mnemonic_activate function to option menu.
 
-2001-10-15  Murray Cumming <murrayc@usa.net
+2001-10-15  Murray Cumming <murrayc@usa.net>
 
        * gtk/gtkinputdialog.c, gtk/gtktoolbar.c,  gtk/gtkwidget.c:
          Corrected the type names used in some calls to g_signal_new().
index 3b48967b838e282f8383176166ddf708f41f57fa..072e49aa662b465f262d711150da8aa9d3fe0eba 100644 (file)
@@ -1,9 +1,48 @@
+Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
+       a utility function to translate coordinates relative to
+       one widget's allocation to coordinates relative to another
+       widget's allocation.
+
+       * gtk/gtkradiobutton.c: Add a special ->focus() implementation
+       that:
+        - only accepts external focus if there is no active
+          member of the group or the button is active.
+        - makes arrow keys move the active button as well
+          as the focus
+        - make tab tab out directly.
+       This makes a radio button group act as a single focus location.
+       (#53577).
+       
+       * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
+       it was only a small optimization that didn't matter and made
+       things more complicated.
+
+       * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
+       sorter for FOCUS_TAB as we did for the other focus directions,
+       sort by center of widgets, not upper-left corner. (Shouldn't
+       matter in general.)
+
+       * gtk/gtkcontainer.c: Restructure code to remove duplicate code
+       from the different types of focusing: encapsulate sorting the
+       widgets for the focus direction into one routine
+       (gtk_container_focus_sort()) and then share the work of moving the
+       focus between the different focus directions.
+
+       * gtk/gtkcontainer.c: Fix bug where arrow navigation might not
+       work correctly with focus chains containing non-immediate
+       children. Sorting was being done using allocation coordinates for
+       each widget in the focus chain, and if there were intermediate
+       window-widgets, these allocations would not be in the same
+       coordinate system.
+
 Tue Oct 16 15:12:26 2001  Jonathan Blandford  <jrb@redhat.com>
 
        * gtk/gtkoptionmenu.c (gtk_option_menu_mnemonic_activate): add a
        mnemonic_activate function to option menu.
 
-2001-10-15  Murray Cumming <murrayc@usa.net
+2001-10-15  Murray Cumming <murrayc@usa.net>
 
        * gtk/gtkinputdialog.c, gtk/gtktoolbar.c,  gtk/gtkwidget.c:
          Corrected the type names used in some calls to g_signal_new().
index 3b48967b838e282f8383176166ddf708f41f57fa..072e49aa662b465f262d711150da8aa9d3fe0eba 100644 (file)
@@ -1,9 +1,48 @@
+Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
+       a utility function to translate coordinates relative to
+       one widget's allocation to coordinates relative to another
+       widget's allocation.
+
+       * gtk/gtkradiobutton.c: Add a special ->focus() implementation
+       that:
+        - only accepts external focus if there is no active
+          member of the group or the button is active.
+        - makes arrow keys move the active button as well
+          as the focus
+        - make tab tab out directly.
+       This makes a radio button group act as a single focus location.
+       (#53577).
+       
+       * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
+       it was only a small optimization that didn't matter and made
+       things more complicated.
+
+       * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
+       sorter for FOCUS_TAB as we did for the other focus directions,
+       sort by center of widgets, not upper-left corner. (Shouldn't
+       matter in general.)
+
+       * gtk/gtkcontainer.c: Restructure code to remove duplicate code
+       from the different types of focusing: encapsulate sorting the
+       widgets for the focus direction into one routine
+       (gtk_container_focus_sort()) and then share the work of moving the
+       focus between the different focus directions.
+
+       * gtk/gtkcontainer.c: Fix bug where arrow navigation might not
+       work correctly with focus chains containing non-immediate
+       children. Sorting was being done using allocation coordinates for
+       each widget in the focus chain, and if there were intermediate
+       window-widgets, these allocations would not be in the same
+       coordinate system.
+
 Tue Oct 16 15:12:26 2001  Jonathan Blandford  <jrb@redhat.com>
 
        * gtk/gtkoptionmenu.c (gtk_option_menu_mnemonic_activate): add a
        mnemonic_activate function to option menu.
 
-2001-10-15  Murray Cumming <murrayc@usa.net
+2001-10-15  Murray Cumming <murrayc@usa.net>
 
        * gtk/gtkinputdialog.c, gtk/gtktoolbar.c,  gtk/gtkwidget.c:
          Corrected the type names used in some calls to g_signal_new().
index 3b48967b838e282f8383176166ddf708f41f57fa..072e49aa662b465f262d711150da8aa9d3fe0eba 100644 (file)
@@ -1,9 +1,48 @@
+Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
+       a utility function to translate coordinates relative to
+       one widget's allocation to coordinates relative to another
+       widget's allocation.
+
+       * gtk/gtkradiobutton.c: Add a special ->focus() implementation
+       that:
+        - only accepts external focus if there is no active
+          member of the group or the button is active.
+        - makes arrow keys move the active button as well
+          as the focus
+        - make tab tab out directly.
+       This makes a radio button group act as a single focus location.
+       (#53577).
+       
+       * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
+       it was only a small optimization that didn't matter and made
+       things more complicated.
+
+       * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
+       sorter for FOCUS_TAB as we did for the other focus directions,
+       sort by center of widgets, not upper-left corner. (Shouldn't
+       matter in general.)
+
+       * gtk/gtkcontainer.c: Restructure code to remove duplicate code
+       from the different types of focusing: encapsulate sorting the
+       widgets for the focus direction into one routine
+       (gtk_container_focus_sort()) and then share the work of moving the
+       focus between the different focus directions.
+
+       * gtk/gtkcontainer.c: Fix bug where arrow navigation might not
+       work correctly with focus chains containing non-immediate
+       children. Sorting was being done using allocation coordinates for
+       each widget in the focus chain, and if there were intermediate
+       window-widgets, these allocations would not be in the same
+       coordinate system.
+
 Tue Oct 16 15:12:26 2001  Jonathan Blandford  <jrb@redhat.com>
 
        * gtk/gtkoptionmenu.c (gtk_option_menu_mnemonic_activate): add a
        mnemonic_activate function to option menu.
 
-2001-10-15  Murray Cumming <murrayc@usa.net
+2001-10-15  Murray Cumming <murrayc@usa.net>
 
        * gtk/gtkinputdialog.c, gtk/gtktoolbar.c,  gtk/gtkwidget.c:
          Corrected the type names used in some calls to g_signal_new().
index 3b48967b838e282f8383176166ddf708f41f57fa..072e49aa662b465f262d711150da8aa9d3fe0eba 100644 (file)
@@ -1,9 +1,48 @@
+Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
+       a utility function to translate coordinates relative to
+       one widget's allocation to coordinates relative to another
+       widget's allocation.
+
+       * gtk/gtkradiobutton.c: Add a special ->focus() implementation
+       that:
+        - only accepts external focus if there is no active
+          member of the group or the button is active.
+        - makes arrow keys move the active button as well
+          as the focus
+        - make tab tab out directly.
+       This makes a radio button group act as a single focus location.
+       (#53577).
+       
+       * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
+       it was only a small optimization that didn't matter and made
+       things more complicated.
+
+       * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
+       sorter for FOCUS_TAB as we did for the other focus directions,
+       sort by center of widgets, not upper-left corner. (Shouldn't
+       matter in general.)
+
+       * gtk/gtkcontainer.c: Restructure code to remove duplicate code
+       from the different types of focusing: encapsulate sorting the
+       widgets for the focus direction into one routine
+       (gtk_container_focus_sort()) and then share the work of moving the
+       focus between the different focus directions.
+
+       * gtk/gtkcontainer.c: Fix bug where arrow navigation might not
+       work correctly with focus chains containing non-immediate
+       children. Sorting was being done using allocation coordinates for
+       each widget in the focus chain, and if there were intermediate
+       window-widgets, these allocations would not be in the same
+       coordinate system.
+
 Tue Oct 16 15:12:26 2001  Jonathan Blandford  <jrb@redhat.com>
 
        * gtk/gtkoptionmenu.c (gtk_option_menu_mnemonic_activate): add a
        mnemonic_activate function to option menu.
 
-2001-10-15  Murray Cumming <murrayc@usa.net
+2001-10-15  Murray Cumming <murrayc@usa.net>
 
        * gtk/gtkinputdialog.c, gtk/gtktoolbar.c,  gtk/gtkwidget.c:
          Corrected the type names used in some calls to g_signal_new().
index 3b48967b838e282f8383176166ddf708f41f57fa..072e49aa662b465f262d711150da8aa9d3fe0eba 100644 (file)
@@ -1,9 +1,48 @@
+Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
+       a utility function to translate coordinates relative to
+       one widget's allocation to coordinates relative to another
+       widget's allocation.
+
+       * gtk/gtkradiobutton.c: Add a special ->focus() implementation
+       that:
+        - only accepts external focus if there is no active
+          member of the group or the button is active.
+        - makes arrow keys move the active button as well
+          as the focus
+        - make tab tab out directly.
+       This makes a radio button group act as a single focus location.
+       (#53577).
+       
+       * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
+       it was only a small optimization that didn't matter and made
+       things more complicated.
+
+       * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
+       sorter for FOCUS_TAB as we did for the other focus directions,
+       sort by center of widgets, not upper-left corner. (Shouldn't
+       matter in general.)
+
+       * gtk/gtkcontainer.c: Restructure code to remove duplicate code
+       from the different types of focusing: encapsulate sorting the
+       widgets for the focus direction into one routine
+       (gtk_container_focus_sort()) and then share the work of moving the
+       focus between the different focus directions.
+
+       * gtk/gtkcontainer.c: Fix bug where arrow navigation might not
+       work correctly with focus chains containing non-immediate
+       children. Sorting was being done using allocation coordinates for
+       each widget in the focus chain, and if there were intermediate
+       window-widgets, these allocations would not be in the same
+       coordinate system.
+
 Tue Oct 16 15:12:26 2001  Jonathan Blandford  <jrb@redhat.com>
 
        * gtk/gtkoptionmenu.c (gtk_option_menu_mnemonic_activate): add a
        mnemonic_activate function to option menu.
 
-2001-10-15  Murray Cumming <murrayc@usa.net
+2001-10-15  Murray Cumming <murrayc@usa.net>
 
        * gtk/gtkinputdialog.c, gtk/gtktoolbar.c,  gtk/gtkwidget.c:
          Corrected the type names used in some calls to g_signal_new().
index 3b48967b838e282f8383176166ddf708f41f57fa..072e49aa662b465f262d711150da8aa9d3fe0eba 100644 (file)
@@ -1,9 +1,48 @@
+Tue Oct 16 15:50:03 2001  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c (gtk_widget_translate_coordinates): Add
+       a utility function to translate coordinates relative to
+       one widget's allocation to coordinates relative to another
+       widget's allocation.
+
+       * gtk/gtkradiobutton.c: Add a special ->focus() implementation
+       that:
+        - only accepts external focus if there is no active
+          member of the group or the button is active.
+        - makes arrow keys move the active button as well
+          as the focus
+        - make tab tab out directly.
+       This makes a radio button group act as a single focus location.
+       (#53577).
+       
+       * gtk/gtkcontainer.c (gtk_container_focus): Remove prefiltering -
+       it was only a small optimization that didn't matter and made
+       things more complicated.
+
+       * gtk/gtkcontainer.c (gtk_container_focus_tab): Get rid of custom
+       sorter for FOCUS_TAB as we did for the other focus directions,
+       sort by center of widgets, not upper-left corner. (Shouldn't
+       matter in general.)
+
+       * gtk/gtkcontainer.c: Restructure code to remove duplicate code
+       from the different types of focusing: encapsulate sorting the
+       widgets for the focus direction into one routine
+       (gtk_container_focus_sort()) and then share the work of moving the
+       focus between the different focus directions.
+
+       * gtk/gtkcontainer.c: Fix bug where arrow navigation might not
+       work correctly with focus chains containing non-immediate
+       children. Sorting was being done using allocation coordinates for
+       each widget in the focus chain, and if there were intermediate
+       window-widgets, these allocations would not be in the same
+       coordinate system.
+
 Tue Oct 16 15:12:26 2001  Jonathan Blandford  <jrb@redhat.com>
 
        * gtk/gtkoptionmenu.c (gtk_option_menu_mnemonic_activate): add a
        mnemonic_activate function to option menu.
 
-2001-10-15  Murray Cumming <murrayc@usa.net
+2001-10-15  Murray Cumming <murrayc@usa.net>
 
        * gtk/gtkinputdialog.c, gtk/gtktoolbar.c,  gtk/gtkwidget.c:
          Corrected the type names used in some calls to g_signal_new().
index 1a2858985898847f05cf442ac36b38d4ea80b680..4399afa56016f4c0c806110cad140bb98d35ea89 100644 (file)
@@ -80,15 +80,10 @@ static gboolean gtk_container_focus                (GtkWidget         *widget,
                                                    GtkDirectionType   direction);
 static void     gtk_container_real_set_focus_child (GtkContainer      *container,
                                                    GtkWidget         *widget);
-static gboolean gtk_container_focus_tab            (GtkContainer      *container,
+static GList *  gtk_container_focus_sort           (GtkContainer      *container,
                                                    GList             *children,
                                                    GtkDirectionType   direction);
-static gboolean gtk_container_focus_up_down        (GtkContainer      *container,
-                                                   GList            **children,
-                                                   GtkDirectionType   direction);
-static gboolean gtk_container_focus_left_right     (GtkContainer      *container,
-                                                   GList            **children,
-                                                   GtkDirectionType   direction);
+
 static gboolean gtk_container_focus_move           (GtkContainer      *container,
                                                    GList             *children,
                                                    GtkDirectionType   direction);
@@ -1536,38 +1531,12 @@ get_focus_chain (GtkContainer *container)
   return g_object_get_data (G_OBJECT (container), "gtk-container-focus-chain");
 }
 
-static GList*
-filter_unfocusable (GtkContainer *container,
-                    GList        *list)
-{
-  GList *tmp_list;
-  GList *tmp_list2;
-  
-  tmp_list = list;
-  while (tmp_list)
-    {
-      if (GTK_WIDGET_IS_SENSITIVE (tmp_list->data) &&
-          GTK_WIDGET_DRAWABLE (tmp_list->data) &&
-          (GTK_IS_CONTAINER (tmp_list->data) || GTK_WIDGET_CAN_FOCUS (tmp_list->data)))
-        tmp_list = tmp_list->next;
-      else
-        {
-          tmp_list2 = tmp_list;
-          tmp_list = tmp_list->next;
-          
-          list = g_list_remove_link (list, tmp_list2);
-          g_list_free_1 (tmp_list2);
-        }
-    }
-
-  return list;
-}
-
 static gboolean
 gtk_container_focus (GtkWidget        *widget,
                      GtkDirectionType  direction)
 {
   GList *children;
+  GList *sorted_children;
   gint return_val;
   GtkContainer *container;
 
@@ -1591,183 +1560,141 @@ gtk_container_focus (GtkWidget        *widget,
        * chain to override.
        */
       if (container->has_focus_chain)
-        {
-          children = g_list_copy (get_focus_chain (container));
-        }
+       children = g_list_copy (get_focus_chain (container));
       else
-        {
-          children = NULL;
-          gtk_container_forall (container,
-                                gtk_container_children_callback,
-                                &children);
-          children = g_list_reverse (children);
-        }
+       children = gtk_container_get_children (container);
 
-      if (children)
+      if (container->has_focus_chain &&
+         (direction == GTK_DIR_TAB_FORWARD ||
+          direction == GTK_DIR_TAB_BACKWARD))
        {
-         /* Remove any children which are inappropriate for focus movement
-          */
-          children = filter_unfocusable (container, children);
-          
-         switch (direction)
-           {
-           case GTK_DIR_TAB_FORWARD:
-           case GTK_DIR_TAB_BACKWARD:
-              if (container->has_focus_chain)
-                {
-                  if (direction == GTK_DIR_TAB_BACKWARD)
-                    children = g_list_reverse (children);
-                  return_val = gtk_container_focus_move (container, children, direction);
-                }
-              else
-                return_val = gtk_container_focus_tab (container, children, direction);
-             break;
-           case GTK_DIR_UP:
-           case GTK_DIR_DOWN:
-             return_val = gtk_container_focus_up_down (container, &children, direction);
-             break;
-           case GTK_DIR_LEFT:
-           case GTK_DIR_RIGHT:
-             return_val = gtk_container_focus_left_right (container, &children, direction);
-             break;
-           }
-
-         g_list_free (children);
+         sorted_children = g_list_copy (children);
+         
+         if (direction == GTK_DIR_TAB_BACKWARD)
+           sorted_children = g_list_reverse (sorted_children);
        }
+      else
+       sorted_children = gtk_container_focus_sort (container, children, direction);
+      
+      return_val = gtk_container_focus_move (container, sorted_children, direction);
+
+      g_list_free (sorted_children);
+      g_list_free (children);
     }
 
   return return_val;
 }
 
-static gboolean
-gtk_container_focus_tab (GtkContainer     *container,
-                        GList            *children,
-                        GtkDirectionType  direction)
+static gint
+tab_compare (gconstpointer a,
+            gconstpointer b)
 {
-  GtkWidget *child;
-  GtkWidget *child2;
-  GList *tmp_list;
-  guint length;
-  guint i, j;
+  const GtkWidget *child1 = a;
+  const GtkWidget *child2 = b;
 
-  length = g_list_length (children);
+  gint y1 = child1->allocation.y + child1->allocation.height / 2;
+  gint y2 = child2->allocation.y + child2->allocation.height / 2;
 
-  /* sort the children in the y direction */
-  for (i = 1; i < length; i++)
+  if (y1 == y2)
     {
-      j = i;
-      tmp_list = g_list_nth (children, j);
-      child = tmp_list->data;
-
-      while (j > 0)
-       {
-         child2 = tmp_list->prev->data;
-         if (child->allocation.y < child2->allocation.y)
-           {
-             tmp_list->data = tmp_list->prev->data;
-             tmp_list = tmp_list->prev;
-             j--;
-           }
-         else
-           break;
-       }
-
-      tmp_list->data = child;
+      gint x1 = child1->allocation.x + child1->allocation.width / 2;
+      gint x2 = child2->allocation.x + child2->allocation.width / 2;
+      
+      return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
     }
+  else
+    return (y1 < y2) ? -1 : 1;
+}
 
-  /* sort the children in the x direction while
-   *  maintaining the y direction sort.
+static GList *
+gtk_container_focus_sort_tab (GtkContainer     *container,
+                             GList            *children,
+                             GtkDirectionType  direction)
+{
+  children = g_list_sort (children, tab_compare);
+
+  /* if we are going backwards then reverse the order
+   *  of the children.
    */
-  for (i = 1; i < length; i++)
+  if (direction == GTK_DIR_TAB_BACKWARD)
+    children = g_list_reverse (children);
+
+  return children;
+}
+
+/* Get coordinates of @widget's allocation with respect to
+ * allocation of @container.
+ */
+static gboolean
+get_allocation_coords (GtkContainer  *container,
+                      GtkWidget     *widget,
+                      GdkRectangle  *allocation)
+{
+  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+
+  *allocation = widget->allocation;
+
+  return gtk_widget_translate_coordinates (widget, GTK_WIDGET (container),
+                                          0, 0, &allocation->x, &allocation->y);
+}
+
+/* Look for a child in @children that is intermediate between
+ * the focus widget and container. This widget, if it exists,
+ * acts as the starting widget for focus navigation.
+ */
+static GtkWidget *
+find_old_focus (GtkContainer *container,
+               GList        *children)
+{
+  GList *tmp_list = children;
+  while (tmp_list)
     {
-      j = i;
-      tmp_list = g_list_nth (children, j);
-      child = tmp_list->data;
+      GtkWidget *child = tmp_list->data;
+      GtkWidget *widget = child;
 
-      while (j > 0)
+      while (widget && widget != (GtkWidget *)container)
        {
-         child2 = tmp_list->prev->data;
-         if ((child->allocation.x < child2->allocation.x) &&
-             (child->allocation.y == child2->allocation.y))
-           {
-             tmp_list->data = tmp_list->prev->data;
-             tmp_list = tmp_list->prev;
-             j--;
-           }
-         else
-           break;
+         GtkWidget *parent = widget->parent;
+         if (parent && ((GtkContainer *)parent)->focus_child != widget)
+           goto next;
+
+         widget = parent;
        }
 
-      tmp_list->data = child;
-    }
+      return child;
 
-  /* if we are going backwards then reverse the order
-   *  of the children.
-   */
-  if (direction == GTK_DIR_TAB_BACKWARD)
-    children = g_list_reverse (children);
+    next:
+      tmp_list = tmp_list->next;
+    }
 
-  return gtk_container_focus_move (container, children, direction);
+  return NULL;
 }
 
 static gboolean
-old_focus_coords (GtkContainer *container, GdkRectangle *old_focus_rect)
+old_focus_coords (GtkContainer *container,
+                 GdkRectangle *old_focus_rect)
 {
   GtkWidget *widget = GTK_WIDGET (container);
   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
   
-  if (toplevel &&
-      GTK_IS_WINDOW (toplevel) && GTK_WINDOW (toplevel)->focus_widget &&
-      GTK_WIDGET_REALIZED (container) &&
-      GTK_WIDGET_REALIZED (GTK_WINDOW (toplevel)->focus_widget))
+  if (toplevel && GTK_IS_WINDOW (toplevel) && GTK_WINDOW (toplevel)->focus_widget)
     {
       GtkWidget *old_focus = GTK_WINDOW (toplevel)->focus_widget;
-      GdkWindow *old_parent_window = old_focus->parent ? old_focus->parent->window : old_focus->window;
-      GdkWindow *new_parent_window = widget->window;
-      GdkWindow *toplevel_window = toplevel->window;
-      
-      *old_focus_rect = old_focus->allocation;
       
-      /* Translate coordinates to the toplevel */
-      
-      while (old_parent_window != toplevel_window)
-       {
-         gint dx, dy;
-         
-         gdk_window_get_position (old_parent_window, &dx, &dy);
-         
-         old_focus_rect->x += dx;
-         old_focus_rect->y += dy;
-         
-         old_parent_window = gdk_window_get_parent (old_parent_window);
-       }
-      
-      /* Translate coordinates back to the new container */
-      
-      while (new_parent_window != toplevel_window)
-       {
-         gint dx, dy;
-         
-         gdk_window_get_position (new_parent_window, &dx, &dy);
-         
-         old_focus_rect->x -= dx;
-         old_focus_rect->y -= dy;
-         
-         new_parent_window = gdk_window_get_parent (new_parent_window);
-       }
-
-      return TRUE;
+      return get_allocation_coords (container, old_focus, old_focus_rect);
     }
-
-  return FALSE;
+  else
+    return FALSE;
 }
 
 typedef struct _CompareInfo CompareInfo;
 
 struct _CompareInfo
 {
+  GtkContainer *container;
   gint x;
   gint y;
+  gboolean reverse;
 };
 
 static gint
@@ -1775,68 +1702,83 @@ up_down_compare (gconstpointer a,
                 gconstpointer b,
                 gpointer      data)
 {
-  const GtkWidget *child1 = a;
-  const GtkWidget *child2 = b;
+  GdkRectangle allocation1;
+  GdkRectangle allocation2;
   CompareInfo *compare = data;
+  gint y1, y2;
 
-  gint y1 = child1->allocation.y + child1->allocation.height / 2;
-  gint y2 = child2->allocation.y + child2->allocation.height / 2;
+  get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
+  get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
+
+  y1 = allocation1.y + allocation1.height / 2;
+  y2 = allocation2.y + allocation2.height / 2;
 
   if (y1 == y2)
     {
-      gint x1 = abs (child1->allocation.x + child1->allocation.width / 2 - compare->x);
-      gint x2 = abs (child2->allocation.x + child2->allocation.width / 2 - compare->x);
+      gint x1 = abs (allocation1.x + allocation1.width / 2 - compare->x);
+      gint x2 = abs (allocation2.x + allocation2.width / 2 - compare->x);
 
-      if (compare->y < y1)
-       return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
-      else
+      if (compare->reverse)
        return (x1 < x2) ? 1 : ((x1 == x2) ? 0 : -1);
+      else
+       return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
     }
   else
     return (y1 < y2) ? -1 : 1;
 }
 
-static gboolean
-gtk_container_focus_up_down (GtkContainer     *container,
-                            GList           **children,
-                            GtkDirectionType  direction)
+static GList *
+gtk_container_focus_sort_up_down (GtkContainer     *container,
+                                 GList            *children,
+                                 GtkDirectionType  direction)
 {
   CompareInfo compare;
   GList *tmp_list;
+  GtkWidget *old_focus;
 
-  if (container->focus_child)
+  compare.container = container;
+  compare.reverse = (direction == GTK_DIR_UP);
+
+  old_focus = find_old_focus (container, children);
+  if (old_focus)
     {
+      GdkRectangle old_allocation;
       gint compare_x1;
       gint compare_x2;
       gint compare_y;
-      
+
       /* Delete widgets from list that don't match minimum criteria */
 
-      compare_x1 = container->focus_child->allocation.x;
-      compare_x2 = container->focus_child->allocation.x + container->focus_child->allocation.width;
+      get_allocation_coords (container, old_focus, &old_allocation);
+
+      compare_x1 = old_allocation.x;
+      compare_x2 = old_allocation.x + old_allocation.width;
 
       if (direction == GTK_DIR_UP)
-       compare_y = container->focus_child->allocation.y;
+       compare_y = old_allocation.y;
       else
-       compare_y = container->focus_child->allocation.y + container->focus_child->allocation.height;
+       compare_y = old_allocation.y + old_allocation.height;
       
-      tmp_list = *children;
+      tmp_list = children;
       while (tmp_list)
        {
          GtkWidget *child = tmp_list->data;
          GList *next = tmp_list->next;
          gint child_x1, child_x2;
+         GdkRectangle child_allocation;
          
-         if (child != container->focus_child)
+         if (child != old_focus)
            {
-             child_x1 = child->allocation.x;
-             child_x2 = child->allocation.x + child->allocation.width;
+             get_allocation_coords (container, child, &child_allocation);
+             
+             child_x1 = child_allocation.x;
+             child_x2 = child_allocation.x + child_allocation.width;
              
              if ((child_x2 <= compare_x1 || child_x1 >= compare_x2) /* No horizontal overlap */ ||
-                 (direction == GTK_DIR_DOWN && child->allocation.y + child->allocation.height < compare_y) || /* Not below */
-                 (direction == GTK_DIR_UP && child->allocation.y > compare_y)) /* Not above */
+                 (direction == GTK_DIR_DOWN && child_allocation.y + child_allocation.height < compare_y) || /* Not below */
+                 (direction == GTK_DIR_UP && child_allocation.y > compare_y)) /* Not above */
                {
-                 *children = g_list_delete_link (*children, tmp_list);
+                 children = g_list_delete_link (children, tmp_list);
                }
            }
          
@@ -1844,7 +1786,7 @@ gtk_container_focus_up_down (GtkContainer     *container,
        }
 
       compare.x = (compare_x1 + compare_x2) / 2;
-      compare.y = container->focus_child->allocation.y + container->focus_child->allocation.height / 2;
+      compare.y = old_allocation.y + old_allocation.height / 2;
     }
   else
     {
@@ -1871,12 +1813,12 @@ gtk_container_focus_up_down (GtkContainer     *container,
        compare.y = (direction == GTK_DIR_DOWN) ? 0 : + widget->allocation.height;
     }
 
-  *children = g_list_sort_with_data (*children, up_down_compare, &compare);
+  children = g_list_sort_with_data (children, up_down_compare, &compare);
 
-  if (direction == GTK_DIR_UP)
-    *children = g_list_reverse (*children);
+  if (compare.reverse)
+    children = g_list_reverse (children);
 
-  return gtk_container_focus_move (container, *children, direction);
+  return children;
 }
 
 static gint
@@ -1884,68 +1826,84 @@ left_right_compare (gconstpointer a,
                    gconstpointer b,
                    gpointer      data)
 {
-  const GtkWidget *child1 = a;
-  const GtkWidget *child2 = b;
+  GdkRectangle allocation1;
+  GdkRectangle allocation2;
   CompareInfo *compare = data;
+  gint x1, x2;
+
+  get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
+  get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
 
-  gint x1 = child1->allocation.x + child1->allocation.width / 2;
-  gint x2 = child2->allocation.x + child2->allocation.width / 2;
+  x1 = allocation1.x + allocation1.width / 2;
+  x2 = allocation2.x + allocation2.width / 2;
 
   if (x1 == x2)
     {
-      gint y1 = abs (child1->allocation.y + child1->allocation.height / 2 - compare->y);
-      gint y2 = abs (child2->allocation.y + child2->allocation.height / 2 - compare->y);
+      gint y1 = abs (allocation1.y + allocation1.height / 2 - compare->y);
+      gint y2 = abs (allocation2.y + allocation2.height / 2 - compare->y);
 
-      if (compare->x < x1)
-       return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
-      else
+      if (compare->reverse)
        return (y1 < y2) ? 1 : ((y1 == y2) ? 0 : -1);
+      else
+       return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
     }
   else
     return (x1 < x2) ? -1 : 1;
 }
 
-static gboolean
-gtk_container_focus_left_right (GtkContainer     *container,
-                               GList           **children,
-                               GtkDirectionType  direction)
+static GList *
+gtk_container_focus_sort_left_right (GtkContainer     *container,
+                                    GList            *children,
+                                    GtkDirectionType  direction)
 {
   CompareInfo compare;
   GList *tmp_list;
+  GtkWidget *old_focus;
 
-  if (container->focus_child)
+  compare.container = container;
+  compare.reverse = (direction == GTK_DIR_LEFT);
+  
+  old_focus = find_old_focus (container, children);
+  if (old_focus)
     {
+      GdkRectangle old_allocation;
+
       gint compare_y1;
       gint compare_y2;
       gint compare_x;
       
       /* Delete widgets from list that don't match minimum criteria */
 
-      compare_y1 = container->focus_child->allocation.y;
-      compare_y2 = container->focus_child->allocation.y + container->focus_child->allocation.height;
+      get_allocation_coords (container, old_focus, &old_allocation);
+
+      compare_y1 = old_allocation.y;
+      compare_y2 = old_allocation.y + old_allocation.height;
 
       if (direction == GTK_DIR_LEFT)
-       compare_x = container->focus_child->allocation.x;
+       compare_x = old_allocation.x;
       else
-       compare_x = container->focus_child->allocation.x + container->focus_child->allocation.width;
+       compare_x = old_allocation.x + old_allocation.width;
       
-      tmp_list = *children;
+      tmp_list = children;
       while (tmp_list)
        {
          GtkWidget *child = tmp_list->data;
          GList *next = tmp_list->next;
          gint child_y1, child_y2;
+         GdkRectangle child_allocation;
          
-         if (child != container->focus_child)
+         if (child != old_focus)
            {
-             child_y1 = child->allocation.y;
-             child_y2 = child->allocation.y + child->allocation.height;
+             get_allocation_coords (container, child, &child_allocation);
+             
+             child_y1 = child_allocation.y;
+             child_y2 = child_allocation.y + child_allocation.height;
              
              if ((child_y2 <= compare_y1 || child_y1 >= compare_y2) /* No vertical overlap */ ||
-                 (direction == GTK_DIR_RIGHT && child->allocation.x + child->allocation.width < compare_x) || /* Not to left */
-                 (direction == GTK_DIR_LEFT && child->allocation.x > compare_x)) /* Not to right */
+                 (direction == GTK_DIR_RIGHT && child_allocation.x + child_allocation.width < compare_x) || /* Not to left */
+                 (direction == GTK_DIR_LEFT && child_allocation.x > compare_x)) /* Not to right */
                {
-                 *children = g_list_delete_link (*children, tmp_list);
+                 children = g_list_delete_link (children, tmp_list);
                }
            }
          
@@ -1953,7 +1911,7 @@ gtk_container_focus_left_right (GtkContainer     *container,
        }
 
       compare.y = (compare_y1 + compare_y2) / 2;
-      compare.x = container->focus_child->allocation.x + container->focus_child->allocation.width / 2;
+      compare.x = old_allocation.x + old_allocation.width / 2;
     }
   else
     {
@@ -1980,12 +1938,51 @@ gtk_container_focus_left_right (GtkContainer     *container,
        compare.x = (direction == GTK_DIR_RIGHT) ? 0 : widget->allocation.width;
     }
 
-  *children = g_list_sort_with_data (*children, left_right_compare, &compare);
+  children = g_list_sort_with_data (children, left_right_compare, &compare);
+
+  if (compare.reverse)
+    children = g_list_reverse (children);
+
+  return children;
+}
+
+/**
+ * gtk_container_focus_sort:
+ * @container: a #GtkContainer
+ * @children:  a list of descendents of @container (they don't
+ *             have to be direct children.
+ * @direction: focus direction
+ * 
+ * Sorts @children in the correct order for focusing with
+ * direction type @direction.
+ * 
+ * Return value: a copy of @children, sorted in correct focusing order,
+ *   with children that aren't suitable for focusing in this direction
+ *   removed.
+ **/
+static GList *
+gtk_container_focus_sort (GtkContainer     *container,
+                         GList            *children,
+                         GtkDirectionType  direction)
+{
+  children = g_list_copy (children);
+  
+  switch (direction)
+    {
+    case GTK_DIR_TAB_FORWARD:
+    case GTK_DIR_TAB_BACKWARD:
+      return gtk_container_focus_sort_tab (container, children, direction);
+    case GTK_DIR_UP:
+    case GTK_DIR_DOWN:
+      return gtk_container_focus_sort_up_down (container, children, direction);
+    case GTK_DIR_LEFT:
+    case GTK_DIR_RIGHT:
+      return gtk_container_focus_sort_left_right (container, children, direction);
+    }
 
-  if (direction == GTK_DIR_LEFT)
-    *children = g_list_reverse (*children);
+  g_assert_not_reached ();
 
-  return gtk_container_focus_move (container, *children, direction);
+  return NULL;
 }
 
 static gboolean
index 245bec87e9f39e714e812f650022337014026f8b..e5cf6a318eceeb9726ff431ab78752794cd5a22e 100644 (file)
@@ -35,19 +35,20 @@ enum {
 };
 
 
-static void gtk_radio_button_class_init     (GtkRadioButtonClass  *klass);
-static void gtk_radio_button_init           (GtkRadioButton       *radio_button);
-static void gtk_radio_button_destroy        (GtkObject            *object);
-static void gtk_radio_button_clicked        (GtkButton            *button);
-static void gtk_radio_button_draw_indicator (GtkCheckButton       *check_button,
-                                            GdkRectangle         *area);
-static void gtk_radio_button_set_arg       (GtkObject            *object,
-                                            GtkArg               *arg,
-                                            guint                 arg_id);
-static void gtk_radio_button_get_arg       (GtkObject            *object,
-                                            GtkArg               *arg,
-                                            guint                 arg_id);
-
+static void     gtk_radio_button_class_init     (GtkRadioButtonClass *klass);
+static void     gtk_radio_button_init           (GtkRadioButton      *radio_button);
+static void     gtk_radio_button_destroy        (GtkObject           *object);
+static gboolean gtk_radio_button_focus          (GtkWidget           *widget,
+                                                GtkDirectionType     direction);
+static void     gtk_radio_button_clicked        (GtkButton           *button);
+static void     gtk_radio_button_draw_indicator (GtkCheckButton      *check_button,
+                                                GdkRectangle        *area);
+static void     gtk_radio_button_set_arg        (GtkObject           *object,
+                                                GtkArg              *arg,
+                                                guint                arg_id);
+static void     gtk_radio_button_get_arg        (GtkObject           *object,
+                                                GtkArg              *arg,
+                                                guint                arg_id);
 
 static GtkCheckButtonClass *parent_class = NULL;
 
@@ -83,8 +84,10 @@ gtk_radio_button_class_init (GtkRadioButtonClass *class)
   GtkObjectClass *object_class;
   GtkButtonClass *button_class;
   GtkCheckButtonClass *check_button_class;
+  GtkWidgetClass *widget_class;
 
   object_class = (GtkObjectClass*) class;
+  widget_class = (GtkWidgetClass*) class;
   button_class = (GtkButtonClass*) class;
   check_button_class = (GtkCheckButtonClass*) class;
 
@@ -96,6 +99,8 @@ gtk_radio_button_class_init (GtkRadioButtonClass *class)
   object_class->get_arg = gtk_radio_button_get_arg;
   object_class->destroy = gtk_radio_button_destroy;
 
+  widget_class->focus = gtk_radio_button_focus;
+
   button_class->clicked = gtk_radio_button_clicked;
 
   check_button_class->draw_indicator = gtk_radio_button_draw_indicator;
@@ -330,6 +335,156 @@ gtk_radio_button_destroy (GtkObject *object)
     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
 }
 
+static void
+get_coordinates (GtkWidget    *widget,
+                GtkWidget    *reference,
+                gint         *x,
+                gint         *y)
+{
+  *x = widget->allocation.x + widget->allocation.width / 2;
+  *y = widget->allocation.y + widget->allocation.height / 2;
+  
+  gtk_widget_translate_coordinates (widget, reference, *x, *y, x, y);
+}
+
+static gint
+left_right_compare (gconstpointer a,
+                   gconstpointer b,
+                   gpointer      data)
+{
+  gint x1, y1, x2, y2;
+
+  get_coordinates ((GtkWidget *)a, data, &x1, &y1);
+  get_coordinates ((GtkWidget *)b, data, &x2, &y2);
+
+  if (y1 == y2)
+    return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
+  else
+    return (y1 < y2) ? -1 : 1;
+}
+
+static gint
+up_down_compare (gconstpointer a,
+                gconstpointer b,
+                gpointer      data)
+{
+  gint x1, y1, x2, y2;
+  
+  get_coordinates ((GtkWidget *)a, data, &x1, &y1);
+  get_coordinates ((GtkWidget *)b, data, &x2, &y2);
+  
+  if (x1 == x2)
+    return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
+  else
+    return (x1 < x2) ? -1 : 1;
+}
+
+static gboolean
+gtk_radio_button_focus (GtkWidget         *widget,
+                       GtkDirectionType   direction)
+{
+  GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
+  GSList *tmp_slist;
+  
+  if (gtk_widget_is_focus (widget))
+    {
+      GSList *focus_list, *tmp_list;
+      GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
+      GtkWidget *new_focus = NULL;
+
+      focus_list = g_slist_copy (radio_button->group);
+      
+      switch (direction)
+       {
+       case GTK_DIR_TAB_FORWARD:
+       case GTK_DIR_TAB_BACKWARD:
+         return FALSE;
+       case GTK_DIR_LEFT:
+       case GTK_DIR_RIGHT:
+         focus_list = g_slist_sort_with_data (focus_list, left_right_compare, toplevel);
+         break;
+       case GTK_DIR_UP:
+       case GTK_DIR_DOWN:
+         focus_list = g_slist_sort_with_data (focus_list, up_down_compare, toplevel);
+         break;
+       }
+
+      if (direction == GTK_DIR_LEFT || direction == GTK_DIR_UP)
+       focus_list = g_slist_reverse (focus_list);
+
+      tmp_list = g_slist_find (focus_list, widget);
+
+      if (tmp_list)
+       {
+         tmp_list = tmp_list->next;
+         
+         while (tmp_list)
+           {
+             GtkWidget *child = tmp_list->data;
+             
+             if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_IS_SENSITIVE (child))
+               {
+                 new_focus = child;
+                 break;
+               }
+
+             tmp_list = tmp_list->next;
+           }
+       }
+
+      if (!new_focus)
+       {
+         tmp_list = focus_list;
+
+         while (tmp_list)
+           {
+             GtkWidget *child = tmp_list->data;
+             
+             if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_IS_SENSITIVE (child))
+               {
+                 new_focus = child;
+                 break;
+               }
+             
+             tmp_list = tmp_list->next;
+           }
+       }
+      
+      g_slist_free (focus_list);
+
+      if (new_focus)
+       {
+         gtk_widget_grab_focus (new_focus);
+         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
+       }
+
+      return TRUE;
+    }
+  else
+    {
+      GtkRadioButton *selected_button = NULL;
+      
+      /* We accept the focus if, we don't have the focus and
+       *  - we are the currently active button in the group
+       *  - there is no currently active radio button.
+       */
+      
+      tmp_slist = radio_button->group;
+      while (tmp_slist)
+       {
+         if (GTK_TOGGLE_BUTTON (tmp_slist->data)->active)
+           selected_button = tmp_slist->data;
+         tmp_slist = tmp_slist->next;
+       }
+      
+      if (selected_button && selected_button != radio_button)
+       return FALSE;
+
+      gtk_widget_grab_focus (widget);
+      return TRUE;
+    }
+}
+
 static void
 gtk_radio_button_clicked (GtkButton *button)
 {
index 52f0203573dbf7ed4426b214c5ac2c58d9c28d91..882bd57f3426a0d8840b7ca66e7635bfaf36dcd8 100644 (file)
@@ -2317,6 +2317,167 @@ gtk_widget_size_allocate (GtkWidget     *widget,
     }
 }
 
+/**
+ * gtk_widget_common_ancestor:
+ * @widget_a: a #GtkWidget
+ * @widget_b: a #GtkWidget
+ * 
+ * Find the common ancestor of @widget_a and @widget_b that
+ * is closest to the two widgets.
+ * 
+ * Return value: the closest common ancestor of @widget_a and
+ *   @widget_b or %NULL if @widget_a and @widget_b do not
+ *   share a common ancestor.
+ **/
+static GtkWidget *
+gtk_widget_common_ancestor (GtkWidget *widget_a,
+                           GtkWidget *widget_b)
+{
+  GtkWidget *parent_a;
+  GtkWidget *parent_b;
+  gint depth_a = 0;
+  gint depth_b = 0;
+
+  parent_a = widget_a;
+  while (parent_a->parent)
+    {
+      parent_a = parent_a->parent;
+      depth_a++;
+    }
+
+  parent_b = widget_b;
+  while (parent_b->parent)
+    {
+      parent_b = parent_b->parent;
+      depth_b++;
+    }
+
+  if (parent_a != parent_b)
+    return NULL;
+
+  while (depth_a > depth_b)
+    {
+      widget_a = widget_a->parent;
+      depth_a--;
+    }
+
+  while (depth_b > depth_a)
+    {
+      widget_b = widget_b->parent;
+      depth_b--;
+    }
+
+  while (widget_a != widget_b)
+    {
+      widget_a = widget_a->parent;
+      widget_b = widget_b->parent;
+    }
+
+  return widget_a;
+}
+
+/**
+ * gtk_widget_translate_coordinates:
+ * @src_widget:  a #GtkWidget
+ * @dest_widget: a #GtkWidget
+ * @src_x: X position relative to @src_widget
+ * @src_y: Y position relative to @src_widget
+ * @dest_x: location to store X position relative to @dest_widget
+ * @dest_y: location to store Y position relative to @dest_widget
+ * 
+ * Translate coordinates relative to @src_widget's allocation to coordinates
+ * relative to @dest_widget's allocations. In order to perform this
+ * operation, both widgets must be realized, and must share a common
+ * toplevel.
+ * 
+ * Return value: %FALSE if either widget was not realized, or there
+ *   was no common ancestor. In this case, nothing is stored in
+ *   *@dest_x and *@dest_y. Otherwise %TRUE.
+ **/
+gboolean
+gtk_widget_translate_coordinates (GtkWidget  *src_widget,
+                                 GtkWidget  *dest_widget,
+                                 gint        src_x,
+                                 gint        src_y,
+                                 gint       *dest_x,
+                                 gint       *dest_y)
+{
+  GtkWidget *ancestor;
+  GdkWindow *window;
+
+  g_return_if_fail (GTK_IS_WIDGET (src_widget));
+  g_return_if_fail (GTK_IS_WIDGET (dest_widget));
+
+  ancestor = gtk_widget_common_ancestor (src_widget, dest_widget);
+  if (!ancestor || !GTK_WIDGET_REALIZED (src_widget) || !GTK_WIDGET_REALIZED (dest_widget))
+    return FALSE;
+
+  /* Translate from allocation relative to window relative */
+  if (!GTK_WIDGET_NO_WINDOW (src_widget) && src_widget->parent)
+    {
+      gint wx, wy;
+      gdk_window_get_position (src_widget->window, &wx, &wy);
+
+      src_x -= wx - src_widget->allocation.x;
+      src_y -= wy - src_widget->allocation.y;
+    }
+  else
+    {
+      src_x += src_widget->allocation.x;
+      src_y += src_widget->allocation.y;
+    }
+
+  /* Translate to the common ancestor */
+  window = src_widget->window;
+  while (window != ancestor->window)
+    {
+      gint dx, dy;
+      
+      gdk_window_get_position (window, &dx, &dy);
+      
+      src_x += dx;
+      src_y += dy;
+      
+      window = gdk_window_get_parent (window);
+    }
+
+  /* And back */
+  window = dest_widget->window;
+  while (window != ancestor->window)
+    {
+      gint dx, dy;
+      
+      gdk_window_get_position (window, &dx, &dy);
+      
+      src_x -= dx;
+      src_y -= dy;
+      
+      window = gdk_window_get_parent (window);
+    }
+
+  /* Translate from window relative to allocation relative */
+  if (!GTK_WIDGET_NO_WINDOW (dest_widget) && dest_widget->parent)
+    {
+      gint wx, wy;
+      gdk_window_get_position (dest_widget->window, &wx, &wy);
+
+      src_x += wx - dest_widget->allocation.x;
+      src_y += wy - dest_widget->allocation.y;
+    }
+  else
+    {
+      src_x -= dest_widget->allocation.x;
+      src_y -= dest_widget->allocation.y;
+    }
+
+  if (dest_x)
+    *dest_x = src_x;
+  if (dest_y)
+    *dest_y = src_y;
+
+  return TRUE;
+}
+
 static void
 gtk_widget_real_size_allocate (GtkWidget     *widget,
                               GtkAllocation *allocation)
index 60997c9118f9ba5a497892474c9dc00dbbb69b92..111c7792689b38bf810c824901b1d28ea19ebd8b 100644 (file)
@@ -621,6 +621,13 @@ void            gtk_widget_get_pointer     (GtkWidget      *widget,
 gboolean     gtk_widget_is_ancestor    (GtkWidget      *widget,
                                         GtkWidget      *ancestor);
 
+gboolean     gtk_widget_translate_coordinates (GtkWidget  *src_widget,
+                                              GtkWidget  *dest_widget,
+                                              gint        src_x,
+                                              gint        src_y,
+                                              gint       *dest_x,
+                                              gint       *dest_y);
+
 /* Hide widget and return TRUE.
  */
 gboolean     gtk_widget_hide_on_delete (GtkWidget      *widget);